/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package neuronalenetzeelearning.view.gui;

import java.io.ByteArrayInputStream;
import javax.swing.JFrame;

import javax.swing.JPanel;
import javax.swing.WindowConstants;
import neuronalenetzeelearning.dataConnectors.GraphMLDocumentBuilder;
import neuronalenetzeelearning.model.WindowGUI;
import prefuse.data.*;
import prefuse.data.io.*;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.render.*;
import prefuse.util.*;
import prefuse.action.assignment.*;
import prefuse.Constants;
import prefuse.visual.*;
import prefuse.action.*;
import prefuse.action.layout.SpecifiedLayout;
import prefuse.activity.*;
import prefuse.action.layout.graph.*;
import prefuse.controls.*;

/**
 *
 * @author svenmaster
 */
public class GraphVisualization {

    private neuronalenetzeelearning.model.Graph dataGraph;
    private Graph graph;
    private Visualization visualization;
    private Display display;

    /**
     * This class is used for creation of a visualization using prefuse and wrapping it to GUI elements, like a JPanel.
     * @param dataGraph This will be the initial graph (from the data model) to be displayed, so that the learner will take notice about the "look & feel" of this tool.
     */
    public GraphVisualization(neuronalenetzeelearning.model.Graph dataGraph) {
        this.dataGraph = dataGraph;
        // 1. Load the data
        graph = null;
        /* graph will contain the core data */
        try {
            String xmlString = (GraphMLDocumentBuilder.convertToGraphMLAsString(dataGraph));
            ByteArrayInputStream inputStream = new ByteArrayInputStream(xmlString.getBytes());
            graph = new GraphMLReader().readGraph(inputStream);
            /* load the data from an XML file */
        } catch (DataIOException e) {
            e.printStackTrace();
            System.err.println("Error loading graph. Exiting...");
            System.exit(1);
        }

        // 2. prepare the visualization

        visualization = new Visualization();
        /* visualization is the main object that will run the visualization */
        visualization.add("nnel", graph);
        /* add our data to the visualization */

        // 3. setup the renderers and the render factory

        // labels for name
        LabelRenderer nameLabel = new LabelRenderer("name");
        nameLabel.setRoundedCorner(8, 8);
        /* nameLabel decribes how to draw the data elements labeled as "name" */

        //adding an EdgeRenderer to draw arrows
        EdgeRenderer edgeRenderer = new EdgeRenderer();
        edgeRenderer.setEdgeType(Constants.EDGE_TYPE_LINE);
        edgeRenderer.setArrowType(Constants.EDGE_ARROW_FORWARD);
        edgeRenderer.setArrowHeadSize(5, 5);
        edgeRenderer.setRenderType(AbstractShapeRenderer.RENDER_TYPE_DRAW_AND_FILL);

        // create the render factory
        RendererFactory rendererFactory = new DefaultRendererFactory(nameLabel, edgeRenderer);
        visualization.setRendererFactory(rendererFactory);

        // 4. process the actions

        // colour palette for nominal data type
        int[] palette = new int[]{ColorLib.rgb(255, 180, 180), ColorLib.rgb(190, 190, 255)};
        /* ColorLib.rgb converts the colour values to integers */

        // map data to colours in the palette
        DataColorAction fill = new DataColorAction("nnel.nodes", "layer", Constants.NOMINAL, VisualItem.FILLCOLOR, palette);
//        Predicate fillPredicate = new
//        fill.add(null, fill)
        /* fill describes what colour to draw the graph based on a portion of the data */

        // node text
        ColorAction text = new ColorAction("nnel.nodes", VisualItem.TEXTCOLOR, ColorLib.gray(0));
        /* text describes what colour to draw the text */

        // edge
        ColorAction edgesStroke = new ColorAction("nnel.edges", VisualItem.STROKECOLOR, ColorLib.gray(100));
        ColorAction edgesFill = new ColorAction("nnel.edges", VisualItem.FILLCOLOR, ColorLib.gray(50));
        /* edge describes what colour to draw the edges */

        // combine the colour assignments into an action list
        ActionList colour = new ActionList();
        colour.add(fill);
        colour.add(text);
        colour.add(edgesStroke);
        colour.add(edgesFill);
        visualization.putAction("colour", colour);
        /* add the colour actions to the visualization */

        //Actions for updating
        ActionList update = new ActionList();
        update.add(colour);
        update.add(edgesFill);
        update.add(edgesStroke);
        update.add(fill);
        update.add(text);
        update.add(new RepaintAction());
        visualization.putAction("update", update);

        // create a separate action list for the layout
        ActionList layout = new ActionList(Activity.INFINITY);
        SpecifiedLayout specifiedLayout = new SpecifiedLayout("nnel.nodes", "xCoordinate", "yCoordinate");
        layout.add(specifiedLayout);

//        Action treeLayout = new NodeLinkTreeLayout("nnel");
//        NeighborHighlightControl layoutAction = new NeighborHighlightControl("nnel");
//        layout.add(layoutAction);
        /* use a force-directed graph layout with default parameters */

        layout.add(new RepaintAction());
        /* repaint after each movement of the graph nodes */

        visualization.putAction("layout", layout);
        /* add the laout actions to the visualization */

        // 5. add interactive controls for visualization

        display = new Display(visualization);
        display.setSize(800, 520);
        display.pan(0, 20);	// pan to the middle
//        display.addControlListener(new DragControl());
        /* allow items to be dragged around */

//        display.addControlListener(new PanControl());
        /* allow the display to be panned (moved left/right, up/down) (left-drag)*/

        display.addControlListener(new ZoomControl());
        /* allow the display to be zoomed (right-drag) */

        NeighborHighlightControl highlightControl = new NeighborHighlightControl();
        display.addControlListener(highlightControl);
        /* highlight neighbour-nodes */

        display.addControlListener(new ToolTipControl("threshold"));
//        display.addControlListener(new ToolTipControl("layer"));
        /* display a tooltip with threshold */

        display.addControlListener(new WheelZoomControl());
        /* allow the display to be zoomed (mousewheel) */

        display.setHighQuality(true);

        //FIXME registering doesnt work
//        registerListeners();
    }

    public Graph getGraph() {
        return graph;
    }

    public void setGraph(Graph graph) {
        this.graph = graph;
    }

    public Display getDisplay() {
        return display;
    }

    public void setDisplay(Display display) {
        this.display = display;
    }

    public Visualization getVisualization() {
        return visualization;
    }

    public void setVisualization(Visualization visualization) {
        this.visualization = visualization;
    }

    /**
     * This method provides a GUI-wrapper for the prefuse-visualization 
     * @return JPanel containing the prefuse-visualization
     */
    public JPanel getVisualisationPanel() {
        JPanel panel = new JPanel();
        // create an update listener that will update the visualization when fired
        UpdateListener lstnr = new UpdateListener() {

            public void update(Object src) {
                visualization.run("update");
            }
        };

        // add the listener to this component
        panel.addComponentListener(lstnr);
        /* frame is the main window */

        panel.add(display);
        /* add the display (which holds the visualization) to the window */

        panel.setVisible(true);

        return panel;
    }

    /**
     * Starts the visualization
     */
    public void startVisualization() {
        /* start the visualization working */
        visualization.run("colour");
        visualization.run("layout");
        visualization.run("update");
    }

    /**
     *
     * @param dataGraph
     * @deprecated This will be deleted in upcoming versions.
     */
    @Deprecated
    public static void runVisualizationStandalone(neuronalenetzeelearning.model.Graph dataGraph) {

        // 1. Load the data
        Graph graph = null;
        /* graph will contain the core data */
        try {
            String xmlString = (GraphMLDocumentBuilder.convertToGraphMLAsString(dataGraph));
            ByteArrayInputStream inputStream = new ByteArrayInputStream(xmlString.getBytes());
            graph = new GraphMLReader().readGraph(inputStream);
            /* load the data from an XML file */
        } catch (DataIOException e) {
            e.printStackTrace();
            System.err.println("Error loading graph. Exiting...");
            System.exit(1);
        }

        // 2. prepare the visualization

        Visualization vis = new Visualization();
        /* visualization is the main object that will run the visualization */
        vis.add("nnel", graph);
        /* add our data to the visualization */

        // 3. setup the renderers and the render factory

        // labels for name
        LabelRenderer nameLabel = new LabelRenderer("name");
        nameLabel.setRoundedCorner(8, 8);
        /* nameLabel decribes how to draw the data elements labeled as "name" */

        //adding an EdgeRenderer to draw arrows
        EdgeRenderer edgeRenderer = new EdgeRenderer();
        edgeRenderer.setEdgeType(Constants.EDGE_TYPE_LINE);
        edgeRenderer.setArrowType(Constants.EDGE_ARROW_FORWARD);
        edgeRenderer.setArrowHeadSize(5, 5);
        edgeRenderer.setRenderType(AbstractShapeRenderer.RENDER_TYPE_DRAW_AND_FILL);

        // create the render factory
        RendererFactory rendererFactory = new DefaultRendererFactory(nameLabel, edgeRenderer);
        vis.setRendererFactory(rendererFactory);

        // 4. process the actions

        // colour palette for nominal data type
        int[] palette = new int[]{ColorLib.rgb(255, 180, 180), ColorLib.rgb(190, 190, 255)};
        /* ColorLib.rgb converts the colour values to integers */

        // map data to colours in the palette
        DataColorAction fill = new DataColorAction("nnel.nodes", "layer", Constants.NOMINAL, VisualItem.FILLCOLOR, palette);
//        Predicate fillPredicate = new 
//        fill.add(null, fill)
        /* fill describes what colour to draw the graph based on a portion of the data */

        // node text
        ColorAction text = new ColorAction("nnel.nodes", VisualItem.TEXTCOLOR, ColorLib.gray(0));
        /* text describes what colour to draw the text */

        // edge
        ColorAction edgesStroke = new ColorAction("nnel.edges", VisualItem.STROKECOLOR, ColorLib.gray(100));
        ColorAction edgesFill = new ColorAction("nnel.edges", VisualItem.FILLCOLOR, ColorLib.gray(50));
        /* edge describes what colour to draw the edges */

        // combine the colour assignments into an action list
        ActionList colour = new ActionList();
        colour.add(fill);
        colour.add(text);
        colour.add(edgesStroke);
        colour.add(edgesFill);
        vis.putAction("colour", colour);
        /* add the colour actions to the visualization */


        // create a separate action list for the layout
        ActionList layout = new ActionList(Activity.INFINITY);
//        layout.add(new ForceDirectedLayout("nnel"));
//        GridLayout gridLayout = new GridLayout("nnel");
//        layout.add(gridLayout);

        //using datafields as source for coodinates
        SpecifiedLayout specifiedLayout = new SpecifiedLayout("nnel.nodes", "xCoordinate", "yCoordinate");
        layout.add(specifiedLayout);

//        Action treeLayout = new NodeLinkTreeLayout("nnel");
//        NeighborHighlightControl layoutAction = new NeighborHighlightControl("nnel");
//        layout.add(layoutAction);
        /* use a force-directed graph layout with default parameters */

        layout.add(new RepaintAction());
        /* repaint after each movement of the graph nodes */

        vis.putAction("layout", layout);
        /* add the laout actions to the visualization */

        // 5. add interactive controls for visualization

        Display display = new Display(vis);
        display.setSize(800, 240);
        display.pan(0, 20);	// pan to the middle
        display.getAutoscrolls();
        display.addControlListener(new DragControl());
        /* allow items to be dragged around */

        display.addControlListener(new PanControl());
        /* allow the display to be panned (moved left/right, up/down) (left-drag)*/

        display.addControlListener(new ZoomControl());
        /* allow the display to be zoomed (right-drag) */

        NeighborHighlightControl highlightControl = new NeighborHighlightControl();
        display.addControlListener(highlightControl);
        /* highlight neighbour-nodes */

        display.addControlListener(new ToolTipControl("threshold"));
//        display.addControlListener(new ToolTipControl("layer"));
        /* display a tooltip with threshold */

        display.addControlListener(new WheelZoomControl());
        /* allow the display to be zoomed (mousewheel) */

        display.setHighQuality(true);

        // 6. launch the visualizer in a JFrame

        JFrame frame = new JFrame("prefuse test: NNEL");
        /* frame is the main window */

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(display);
        /* add the display (which holds the visualization) to the window */

        frame.pack();
        frame.setVisible(true);

        /* start the visualization working */
        vis.run("colour");
        vis.run("layout");
    }

    /**
     *
     * @param dataGraph
     * @param window
     * @deprecated Use {@link #getVisualisationPanel()} to retrieve a JPanel and add it to the GUI, run it with {@link #startVisualization()}. <b>This method will be deleted in upcoming versions!</b>
     */
    @Deprecated
    public static void runVisualization(neuronalenetzeelearning.model.Graph dataGraph, WindowGUI window) {

        // 1. Load the data
        Graph graph = null;
        /* graph will contain the core data */
        try {
            String xmlString = (GraphMLDocumentBuilder.convertToGraphMLAsString(dataGraph));
            ByteArrayInputStream inputStream = new ByteArrayInputStream(xmlString.getBytes());
            graph = new GraphMLReader().readGraph(inputStream);
            /* load the data from an XML file */
        } catch (DataIOException e) {
            e.printStackTrace();
            System.err.println("Error loading graph. Exiting...");
            System.exit(1);
        }

        // 2. prepare the visualization

        Visualization vis = new Visualization();
        /* visualization is the main object that will run the visualization */
        vis.add("nnel", graph);
        /* add our data to the visualization */

        // 3. setup the renderers and the render factory

        // labels for name
        LabelRenderer nameLabel = new LabelRenderer("name");
        nameLabel.setRoundedCorner(8, 8);
        /* nameLabel decribes how to draw the data elements labeled as "name" */

        //adding an EdgeRenderer to draw arrows
        EdgeRenderer edgeRenderer = new EdgeRenderer();
        edgeRenderer.setEdgeType(Constants.EDGE_TYPE_LINE);
        edgeRenderer.setArrowType(Constants.EDGE_ARROW_FORWARD);
        edgeRenderer.setArrowHeadSize(5, 5);
        edgeRenderer.setRenderType(AbstractShapeRenderer.RENDER_TYPE_DRAW_AND_FILL);

        // create the render factory
        RendererFactory rendererFactory = new DefaultRendererFactory(nameLabel, edgeRenderer);
        vis.setRendererFactory(rendererFactory);

        // 4. process the actions

        // colour palette for nominal data type
        int[] palette = new int[]{ColorLib.rgb(255, 180, 180), ColorLib.rgb(190, 190, 255)};
        /* ColorLib.rgb converts the colour values to integers */

        // map data to colours in the palette
        DataColorAction fill = new DataColorAction("nnel.nodes", "layer", Constants.NOMINAL, VisualItem.FILLCOLOR, palette);
//        Predicate fillPredicate = new
//        fill.add(null, fill)
        /* fill describes what colour to draw the graph based on a portion of the data */

        // node text
        ColorAction text = new ColorAction("nnel.nodes", VisualItem.TEXTCOLOR, ColorLib.gray(0));
        /* text describes what colour to draw the text */

        // edge
        ColorAction edgesStroke = new ColorAction("nnel.edges", VisualItem.STROKECOLOR, ColorLib.gray(100));
        ColorAction edgesFill = new ColorAction("nnel.edges", VisualItem.FILLCOLOR, ColorLib.gray(50));
        /* edge describes what colour to draw the edges */

        // combine the colour assignments into an action list
        ActionList colour = new ActionList();
        colour.add(fill);
        colour.add(text);
        colour.add(edgesStroke);
        colour.add(edgesFill);
        vis.putAction("colour", colour);
        /* add the colour actions to the visualization */


        // create a separate action list for the layout
        ActionList layout = new ActionList(Activity.INFINITY);
        layout.add(new ForceDirectedLayout("nnel"));
//        Action treeLayout = new NodeLinkTreeLayout("nnel");
//        NeighborHighlightControl layoutAction = new NeighborHighlightControl("nnel");
//        layout.add(layoutAction);
        /* use a force-directed graph layout with default parameters */

        layout.add(new RepaintAction());
        /* repaint after each movement of the graph nodes */

        vis.putAction("layout", layout);
        /* add the laout actions to the visualization */

        // 5. add interactive controls for visualization

        Display display = new Display(vis);
        display.setSize(700, 700);
        display.pan(350, 350);	// pan to the middle
        display.getAutoscrolls();
//        display.addControlListener(new DragControl());
        /* allow items to be dragged around */

//        display.addControlListener(new PanControl());
        /* allow the display to be panned (moved left/right, up/down) (left-drag)*/

        display.addControlListener(new ZoomControl());
        /* allow the display to be zoomed (right-drag) */

        NeighborHighlightControl highlightControl = new NeighborHighlightControl();
        display.addControlListener(highlightControl);
        /* highlight neighbour-nodes */

        display.addControlListener(new ToolTipControl("threshold"));
//        display.addControlListener(new ToolTipControl("layer"));
        /* display a tooltip with threshold */

        display.addControlListener(new WheelZoomControl());
        /* allow the display to be zoomed (mousewheel) */

        display.setHighQuality(true);

        // 6. launch the visualizer in a JFrame

        //JFrame frame = new JFrame("prefuse test: NNEL");
        /* frame is the main window */

        window.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        window.add(display);
        /* add the display (which holds the visualization) to the window */

        window.pack();
        window.setVisible(true);

        /* start the visualization working */
        vis.run("colour");
        vis.run("layout");
    }

    public void registerListeners() {
//        PopupMenuController popup = new PopupMenuController(visualization);
//         display.addControlListener(popup);
//         display.getTextEditor().addKeyListener(popup);
//        for (int i = 0; i < graph.getNodeCount(); i++) {
//            registerNodeListeners(graph.getNode(i));
//        }

    }

    public void registerNodeListeners(Node node) {
       VisualItem item = visualization.getVisualItem("nnel.nodes", node);

    }
    
    public void recalculatePositions() {
        int layer1count = dataGraph.getLayer1Nodes().size();
        int layer2count = dataGraph.getLayer2Nodes().size();

        int xLayer1 = 0;
        int xLayer2 = 0;
        int yLayer1 = 0;
        int yLayer2 = 200;

        int width = 800;

        int xStepLayer1 = width / (layer1count + 1);
        int xStepLayer2 = width / (layer2count + 1);

        for (int i = 0; i < graph.getNodeCount(); i++) {
            Node node = graph.getNode(i);
            if (((String) node.get("layer")).equals("Layer1Node")) {
                xLayer1 = xLayer1 + xStepLayer1;
                node.set("xCoordinate", xLayer1);
                node.set("yCoordinate", yLayer1);
            } else {
                xLayer2 = xLayer2 + xStepLayer2;
                node.set("xCoordinate", xLayer2);
                node.set("yCoordinate", yLayer2);
            }
        }
        visualization.run("update");
        visualization.run("colour");
        visualization.run("layout");
    }

}


